{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "# 简介\n",
    "本文介绍了使用PaddleX对布匹瑕疵检测模型快速训练、使用Paddle-Lite将训练得到的工业质检模型快速部署到Android手机的方法。\n",
    "\n",
    "- 关于工业质检和布匹瑕疵检测数据集的详细介绍请参考该系列文章的第一部分：[CascadeRCNN和YOLOv3_Enhance的布匹瑕疵检测模型训练部署](https://aistudio.baidu.com/aistudio/projectdetail/532715)\n",
    "- 关于部署移动端迁移学习模型的详细过程请参考：[手把手教你部署移动端迁移学习模型（PaddleX、Paddle-Lite）](https://aistudio.baidu.com/aistudio/projectdetail/613622)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 关于本项目\n",
    "\n",
    "> 针对项目还存在的改进空间，以及其它模型在不同移动设备的部署，希望大家多交流观点、介绍经验，共同学习进步。[个人主](https://aistudio.baidu.com/aistudio/personalcenter/thirdview/90149)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "# 拉取工具库"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "!unzip data/data11768/guangdong1_round1_train1_20190818.zip -d data/\n",
    "!unzip data/data11768/guangdong1_round1_testA_20190818.zip -d data/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "!pip install paddlelite\n",
    "!pip install paddlex"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Cloning into 'PaddleDetection'...\n",
      "remote: Enumerating objects: 5704, done.\u001b[K\n",
      "remote: Counting objects: 100% (5704/5704), done.\u001b[K\n",
      "remote: Compressing objects: 100% (2684/2684), done.\u001b[K\n",
      "remote: Total 5704 (delta 4109), reused 4070 (delta 2943), pack-reused 0\u001b[K\n",
      "Receiving objects: 100% (5704/5704), 20.43 MiB | 1.75 MiB/s, done.\n",
      "Resolving deltas: 100% (4109/4109), done.\n",
      "Checking connectivity... done.\n"
     ]
    }
   ],
   "source": [
    "!git clone -b release/0.2 https://gitee.com/paddlepaddle/PaddleDetection.git"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "# 数据集准备"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "defect_name2label = {\n",
    "    '破洞': 1, '水渍': 2, '油渍': 2, '污渍': 2, '三丝': 3, '结头': 4, '花板跳': 5, '百脚': 6, '毛粒': 7,\n",
    "    '粗经': 8, '松经': 9, '断经': 10, '吊经': 11, '粗维': 12, '纬缩': 13, '浆斑': 14, '整经结': 15, '星跳': 16, '跳花': 16,\n",
    "    '断氨纶': 17, '稀密档': 18, '浪纹档': 18, '色差档': 18, '磨痕': 19, '轧痕': 19, '修痕': 19, '烧毛痕': 19, '死皱': 20, '云织': 20,\n",
    "    '双纬': 20, '双经': 20, '跳纱': 20, '筘路': 20, '纬纱不良': 20,\n",
    "}\n",
    "# defect_name2label = {\n",
    "#     '破洞': 1, '水渍': 2, '油渍': 3, '污渍': 4, '三丝': 5, '结头': 6, '花板跳': 7, '百脚': 8, '毛粒': 9,\n",
    "#     '粗经': 10, '松经': 11, '断经': 12, '吊经': 13, '粗维': 14, '纬缩': 15, '浆斑': 16, '整经结': 17, '星跳': 18, '跳花': 19,\n",
    "#     '断氨纶': 20, '稀密档': 21, '浪纹档': 22, '色差档': 23, '磨痕': 24, '轧痕': 25, '修痕': 26, '烧毛痕': 27, '死皱': 28, '云织': 29,\n",
    "#     '双纬': 30, '双经': 31, '跳纱': 32, '筘路': 33, '纬纱不良': 34,\n",
    "# }"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 创建coco格式训练集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "class Fabric2COCO:\r\n",
    "\r\n",
    "    def __init__(self,mode=\"train2017\"):\r\n",
    "    # def __init__(self,mode=\"val\"):\r\n",
    "        self.images = []\r\n",
    "        self.annotations = []\r\n",
    "        self.categories = []\r\n",
    "        self.img_id = 0\r\n",
    "        self.ann_id = 0\r\n",
    "        self.mode =mode\r\n",
    "        # if not os.path.exists(\"coco/images/{}\".format(self.mode)):\r\n",
    "        #     os.makedirs(\"coco/images/{}\".format(self.mode))\r\n",
    "        if not os.path.exists(\"PaddleDetection/dataset/coco/{}\".format(self.mode)):\r\n",
    "            os.makedirs(\"PaddleDetection/dataset/coco/{}\".format(self.mode))\r\n",
    "\r\n",
    "    def to_coco(self, anno_file,img_dir):\r\n",
    "        self._init_categories()\r\n",
    "        anno_result= pd.read_json(open(anno_file,\"r\"))\r\n",
    "        anno_result = anno_result.head(int(anno_result['name'].count()*0.9))\r\n",
    "        # anno_result = anno_result.tail(int(anno_result['name'].count()*0.1)+1)        \r\n",
    "        name_list=anno_result[\"name\"].unique()\r\n",
    "        for img_name in name_list:\r\n",
    "            img_anno = anno_result[anno_result[\"name\"] == img_name]\r\n",
    "            bboxs = img_anno[\"bbox\"].tolist()\r\n",
    "            defect_names = img_anno[\"defect_name\"].tolist()\r\n",
    "            assert img_anno[\"name\"].unique()[0] == img_name\r\n",
    "\r\n",
    "            img_path=os.path.join(img_dir,img_name)\r\n",
    "            # img =cv2.imread(img_path)\r\n",
    "            # h,w,c=img.shape\r\n",
    "            h,w=1000,2446\r\n",
    "            self.images.append(self._image(img_path,h, w))\r\n",
    "\r\n",
    "            self._cp_img(img_path)\r\n",
    "\r\n",
    "            for bbox, defect_name in zip(bboxs, defect_names):\r\n",
    "                label= defect_name2label[defect_name]\r\n",
    "                annotation = self._annotation(label, bbox)\r\n",
    "                self.annotations.append(annotation)\r\n",
    "                self.ann_id += 1\r\n",
    "            self.img_id += 1\r\n",
    "        instance = {}\r\n",
    "        instance['info'] = 'fabric defect'\r\n",
    "        instance['license'] = ['none']\r\n",
    "        instance['images'] = self.images\r\n",
    "        instance['annotations'] = self.annotations\r\n",
    "        instance['categories'] = self.categories\r\n",
    "        return instance\r\n",
    "\r\n",
    "    def _init_categories(self):\r\n",
    "        for v in range(1,21):\r\n",
    "            print(v)\r\n",
    "            category = {}\r\n",
    "            category['id'] = v\r\n",
    "            category['name'] = str(v)\r\n",
    "            category['supercategory'] = 'defect_name'\r\n",
    "            self.categories.append(category)\r\n",
    "        # for k, v in defect_name2label.items():\r\n",
    "        #     category = {}\r\n",
    "        #     category['id'] = v\r\n",
    "        #     category['name'] = k\r\n",
    "        #     category['supercategory'] = 'defect_name'\r\n",
    "        #     self.categories.append(category)\r\n",
    "\r\n",
    "    def _image(self, path,h,w):\r\n",
    "        image = {}\r\n",
    "        image['height'] = h\r\n",
    "        image['width'] = w\r\n",
    "        image['id'] = self.img_id\r\n",
    "        image['file_name'] = os.path.basename(path)\r\n",
    "        return image\r\n",
    "\r\n",
    "    def _annotation(self,label,bbox):\r\n",
    "        area=(bbox[2]-bbox[0])*(bbox[3]-bbox[1])\r\n",
    "        points=[[bbox[0],bbox[1]],[bbox[2],bbox[1]],[bbox[2],bbox[3]],[bbox[0],bbox[3]]]\r\n",
    "        annotation = {}\r\n",
    "        annotation['id'] = self.ann_id\r\n",
    "        annotation['image_id'] = self.img_id\r\n",
    "        annotation['category_id'] = label\r\n",
    "        annotation['segmentation'] = [np.asarray(points).flatten().tolist()]\r\n",
    "        annotation['bbox'] = self._get_box(points)\r\n",
    "        annotation['iscrowd'] = 0\r\n",
    "        annotation['area'] = area\r\n",
    "        return annotation\r\n",
    "\r\n",
    "    def _cp_img(self, img_path):\r\n",
    "        shutil.copy(img_path, os.path.join(\"PaddleDetection/dataset/coco/{}\".format(self.mode), os.path.basename(img_path)))\r\n",
    "        # shutil.copy(img_path, os.path.join(\"coco/images/{}\".format(self.mode), os.path.basename(img_path)))\r\n",
    "    def _get_box(self, points):\r\n",
    "        min_x = min_y = np.inf\r\n",
    "        max_x = max_y = 0\r\n",
    "        for x, y in points:\r\n",
    "            min_x = min(min_x, x)\r\n",
    "            min_y = min(min_y, y)\r\n",
    "            max_x = max(max_x, x)\r\n",
    "            max_y = max(max_y, y)\r\n",
    "        '''coco,[x,y,w,h]'''\r\n",
    "        return [min_x, min_y, max_x - min_x, max_y - min_y]\r\n",
    "    def save_coco_json(self, instance, save_path):\r\n",
    "        import json\r\n",
    "        with open(save_path, 'w') as fp:\r\n",
    "            json.dump(instance, fp, indent=1, separators=(',', ': '))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "'''转换有瑕疵的样本为coco格式'''\r\n",
    "img_dir = \"data/guangdong1_round1_train1_20190818/defect_Images\"\r\n",
    "anno_dir=\"data/guangdong1_round1_train1_20190818/Annotations/anno_train.json\"\r\n",
    "fabric2coco = Fabric2COCO()\r\n",
    "train_instance = fabric2coco.to_coco(anno_dir,img_dir)\r\n",
    "if not os.path.exists(\"PaddleDetection/dataset/coco/annotations/\"):\r\n",
    "    os.makedirs(\"PaddleDetection/dataset/coco/annotations/\")\r\n",
    "fabric2coco.save_coco_json(train_instance, \"PaddleDetection/dataset/coco/annotations/\"+'instances_{}.json'.format(\"train2017\"))\r\n",
    "# fabric2coco.save_coco_json(train_instance, \"coco/annotations/\"+'instances_{}.json'.format(\"val\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 创建coco格式验证集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "class Fabric2COCO:\r\n",
    "\r\n",
    "    def __init__(self,mode=\"val2017\"):\r\n",
    "        self.images = []\r\n",
    "        self.annotations = []\r\n",
    "        self.categories = []\r\n",
    "        self.img_id = 0\r\n",
    "        self.ann_id = 0\r\n",
    "        self.mode =mode\r\n",
    "        # if not os.path.exists(\"coco/images/{}\".format(self.mode)):\r\n",
    "        #     os.makedirs(\"coco/images/{}\".format(self.mode))\r\n",
    "        if not os.path.exists(\"PaddleDetection/dataset/coco/{}\".format(self.mode)):\r\n",
    "            os.makedirs(\"PaddleDetection/dataset/coco/{}\".format(self.mode))\r\n",
    "\r\n",
    "    def to_coco(self, anno_file,img_dir):\r\n",
    "        self._init_categories()\r\n",
    "        anno_result= pd.read_json(open(anno_file,\"r\"))\r\n",
    "        anno_result = anno_result.tail(int(anno_result['name'].count()*0.1)+1)        \r\n",
    "        name_list=anno_result[\"name\"].unique()\r\n",
    "        for img_name in name_list:\r\n",
    "            img_anno = anno_result[anno_result[\"name\"] == img_name]\r\n",
    "            bboxs = img_anno[\"bbox\"].tolist()\r\n",
    "            defect_names = img_anno[\"defect_name\"].tolist()\r\n",
    "            assert img_anno[\"name\"].unique()[0] == img_name\r\n",
    "\r\n",
    "            img_path=os.path.join(img_dir,img_name)\r\n",
    "            # img =cv2.imread(img_path)\r\n",
    "            # h,w,c=img.shape\r\n",
    "            h,w=1000,2446\r\n",
    "            self.images.append(self._image(img_path,h, w))\r\n",
    "\r\n",
    "            self._cp_img(img_path)\r\n",
    "\r\n",
    "            for bbox, defect_name in zip(bboxs, defect_names):\r\n",
    "                label= defect_name2label[defect_name]\r\n",
    "                annotation = self._annotation(label, bbox)\r\n",
    "                self.annotations.append(annotation)\r\n",
    "                self.ann_id += 1\r\n",
    "            self.img_id += 1\r\n",
    "        instance = {}\r\n",
    "        instance['info'] = 'fabric defect'\r\n",
    "        instance['license'] = ['none']\r\n",
    "        instance['images'] = self.images\r\n",
    "        instance['annotations'] = self.annotations\r\n",
    "        instance['categories'] = self.categories\r\n",
    "        return instance\r\n",
    "\r\n",
    "    def _init_categories(self):\r\n",
    "        for v in range(1,21):\r\n",
    "            print(v)\r\n",
    "            category = {}\r\n",
    "            category['id'] = v\r\n",
    "            category['name'] = str(v)\r\n",
    "            category['supercategory'] = 'defect_name'\r\n",
    "            self.categories.append(category)\r\n",
    "        # for k, v in defect_name2label.items():\r\n",
    "        #     category = {}\r\n",
    "        #     category['id'] = v\r\n",
    "        #     category['name'] = k\r\n",
    "        #     category['supercategory'] = 'defect_name'\r\n",
    "        #     self.categories.append(category)\r\n",
    "\r\n",
    "    def _image(self, path,h,w):\r\n",
    "        image = {}\r\n",
    "        image['height'] = h\r\n",
    "        image['width'] = w\r\n",
    "        image['id'] = self.img_id\r\n",
    "        image['file_name'] = os.path.basename(path)\r\n",
    "        return image\r\n",
    "\r\n",
    "    def _annotation(self,label,bbox):\r\n",
    "        area=(bbox[2]-bbox[0])*(bbox[3]-bbox[1])\r\n",
    "        points=[[bbox[0],bbox[1]],[bbox[2],bbox[1]],[bbox[2],bbox[3]],[bbox[0],bbox[3]]]\r\n",
    "        annotation = {}\r\n",
    "        annotation['id'] = self.ann_id\r\n",
    "        annotation['image_id'] = self.img_id\r\n",
    "        annotation['category_id'] = label\r\n",
    "        annotation['segmentation'] = [np.asarray(points).flatten().tolist()]\r\n",
    "        annotation['bbox'] = self._get_box(points)\r\n",
    "        annotation['iscrowd'] = 0\r\n",
    "        annotation['area'] = area\r\n",
    "        return annotation\r\n",
    "\r\n",
    "    def _cp_img(self, img_path):\r\n",
    "        shutil.copy(img_path, os.path.join(\"PaddleDetection/dataset/coco/{}\".format(self.mode), os.path.basename(img_path)))\r\n",
    "        # shutil.copy(img_path, os.path.join(\"coco/images/{}\".format(self.mode), os.path.basename(img_path)))\r\n",
    "    def _get_box(self, points):\r\n",
    "        min_x = min_y = np.inf\r\n",
    "        max_x = max_y = 0\r\n",
    "        for x, y in points:\r\n",
    "            min_x = min(min_x, x)\r\n",
    "            min_y = min(min_y, y)\r\n",
    "            max_x = max(max_x, x)\r\n",
    "            max_y = max(max_y, y)\r\n",
    "        '''coco,[x,y,w,h]'''\r\n",
    "        return [min_x, min_y, max_x - min_x, max_y - min_y]\r\n",
    "    def save_coco_json(self, instance, save_path):\r\n",
    "        import json\r\n",
    "        with open(save_path, 'w') as fp:\r\n",
    "            json.dump(instance, fp, indent=1, separators=(',', ': '))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "'''转换有瑕疵的样本为coco格式'''\r\n",
    "img_dir = \"data/guangdong1_round1_train1_20190818/defect_Images\"\r\n",
    "anno_dir=\"data/guangdong1_round1_train1_20190818/Annotations/anno_train.json\"\r\n",
    "fabric2coco = Fabric2COCO()\r\n",
    "train_instance = fabric2coco.to_coco(anno_dir,img_dir)\r\n",
    "if not os.path.exists(\"PaddleDetection/dataset/coco/annotations/\"):\r\n",
    "    os.makedirs(\"PaddleDetection/dataset/coco/annotations/\")\r\n",
    "fabric2coco.save_coco_json(train_instance, \"PaddleDetection/dataset/coco/annotations/\"+'instances_{}.json'.format(\"val2017\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 整理后最终文件目录格式\n",
    "\n",
    "  ```\n",
    "  ./coco/\n",
    "  ├── annotations\n",
    "  │   ├── instances_train2014.json\n",
    "  │   ├── instances_train2017.json\n",
    "  |   ...\n",
    "  ├── train2017\n",
    "  │   ├── 5cac654fb8e699da1546312852.jpg\n",
    "  │   ├── 14d68415c6f020021536006312.jpg\n",
    "  |   ...\n",
    "  ├── val2017\n",
    "  │   ├── 6cf97c00f1a180120939190444.jpg\n",
    "  │   ├── cb42508af46ed0a50817439434.jpg\n",
    "  |   ..."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 数据集相关计算\n",
    "参考[CascadeRCNN和YOLOv3_Enhance的布匹瑕疵检测模型训练部署](https://aistudio.baidu.com/aistudio/projectdetail/532715)，这里直接给出计算结果\n",
    "```python\n",
    "[[  7.70564186  31.008     ]\n",
    " [ 13.42273099  12.16      ]\n",
    " [570.46606705 141.056     ]\n",
    " [  8.69991823 594.016     ]\n",
    " [ 45.48814391  20.064     ]\n",
    " [  4.22567457  37.696     ]\n",
    " [ 36.0425184  297.312     ]\n",
    " [  5.46852003  94.24      ]\n",
    " [  4.72281276  15.2       ]]\n",
    " \n",
    "Accuracy: 60.76%\n",
    "\n",
    "Boxes:\n",
    " [  7.70564186  13.42273099 570.46606705   8.69991823  45.48814391\n",
    "   4.22567457  36.0425184    5.46852003   4.72281276]-[ 31.008  12.16  141.056 594.016  20.064  37.696 297.312  94.24   15.2  ]\n",
    "   \n",
    "Ratios:\n",
    " [0.01, 0.06, 0.11, 0.12, 0.25, 0.31, 1.1, 2.27, 4.04]\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "# 使用FasterRCNN训练（PaddleX）\n",
    "> 注意：用PaddleX的YoloV3模型进行训练时，一直无法收敛，因此本文仅演示使用`faster_rcnn_r50_vd`快速训练迁移学习模型。\n",
    "## 配置GPU环境"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import os\n",
    "os.environ['CUDA_VISIBLE_DEVICES'] = '0'\n",
    "import paddlex as pdx"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 数据处理与数据增强"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "from paddlex.det import transforms\n",
    "train_transforms = transforms.Compose([\n",
    "    transforms.RandomHorizontalFlip(),\n",
    "    transforms.Normalize(),\n",
    "    transforms.ResizeByShort(short_size=608, max_size=608),\n",
    "    transforms.Padding(coarsest_stride=32)\n",
    "])\n",
    "\n",
    "eval_transforms = transforms.Compose([\n",
    "    transforms.Normalize(),\n",
    "    transforms.ResizeByShort(short_size=608, max_size=608),\n",
    "    transforms.Padding(coarsest_stride=32),\n",
    "])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 创建数据读取器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loading annotations into memory...\n",
      "Done (t=0.04s)\n",
      "creating index...\n",
      "index created!\n",
      "2020-07-20 18:04:48 [INFO]\tStarting to read file list from dataset...\n",
      "2020-07-20 18:04:48 [INFO]\t4290 samples in file PaddleDetection/dataset/coco/annotations/instances_train2017.json\n",
      "loading annotations into memory...\n",
      "Done (t=0.01s)\n",
      "creating index...\n",
      "index created!\n",
      "2020-07-20 18:04:48 [INFO]\tStarting to read file list from dataset...\n",
      "2020-07-20 18:04:48 [INFO]\t484 samples in file PaddleDetection/dataset/coco/annotations/instances_val2017.json\n"
     ]
    }
   ],
   "source": [
    "train_dataset = pdx.datasets.CocoDetection(\n",
    "    data_dir='PaddleDetection/dataset/coco/train2017',\n",
    "    ann_file='PaddleDetection/dataset/coco/annotations/instances_train2017.json',\n",
    "    transforms=train_transforms,\n",
    "    shuffle=True)\n",
    "eval_dataset = pdx.datasets.CocoDetection(\n",
    "    data_dir='PaddleDetection/dataset/coco/val2017',\n",
    "    ann_file='PaddleDetection/dataset/coco/annotations/instances_val2017.json',\n",
    "    transforms=eval_transforms)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "num_classes = len(train_dataset.labels) + 1\n",
    "model = pdx.det.FasterRCNN(backbone='ResNet50_vd',with_fpn=False,num_classes=num_classes)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "model.train(\n",
    "    num_epochs=12,\n",
    "    train_dataset=train_dataset,\n",
    "    train_batch_size=4,\n",
    "    eval_dataset=eval_dataset,\n",
    "    learning_rate=0.0125,\n",
    "    lr_decay_epochs=[8, 11],\n",
    "    save_dir='output/faster_rcnn_r50_vd',\n",
    "    log_interval_steps=200,\n",
    "    use_vdl=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2020-07-12 09:44:19 [INFO]\tStart to evaluating(total_samples=484, total_steps=484)...\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 484/484 [00:59<00:00,  8.15it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "creating index...\n",
      "index created!\n",
      "Running per image evaluation...\n",
      "Evaluate annotation type *bbox*\n",
      "DONE (t=0.46s).\n",
      "Accumulating evaluation results...\n",
      "DONE (t=0.18s).\n",
      " Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.138\n",
      " Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=100 ] = 0.341\n",
      " Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=100 ] = 0.086\n",
      " Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.093\n",
      " Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.103\n",
      " Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.118\n",
      " Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=  1 ] = 0.198\n",
      " Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets= 10 ] = 0.258\n",
      " Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.260\n",
      " Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.173\n",
      " Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.180\n",
      " Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.222\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "OrderedDict([('bbox_mmap', 0.1378795392258368)])"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model.evaluate(eval_dataset, batch_size=1, epoch_id=None, metric=None, return_details=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 模型效果评估\n",
    "![file](https://ai-studio-static-online.cdn.bcebos.com/23d4bda7075a43f197c9c6c37efd3abb73103e4451b44fea857734155951ac2a)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "creating index...\n",
      "index created!\n",
      "creating index...\n",
      "index created!\n",
      "Running per image evaluation...\n",
      "Evaluate annotation type *bbox*\n",
      "DONE (t=0.55s).\n",
      "Accumulating evaluation results...\n",
      "DONE (t=0.09s).\n"
     ]
    }
   ],
   "source": [
    "eval_details_file = 'output/faster_rcnn_r50_vd/epoch_12/eval_details.json'\n",
    "pdx.det.draw_pr_curve(eval_details_file, save_dir='./fabric_flaw_det')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 模型预测"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import paddlex as pdx\n",
    "predictor = pdx.deploy.Predictor('./inference_model/faster_rcnn_r50_vd')\n",
    "result = predictor.predict(image='PaddleDetection/dataset/coco/val2017/9a49f25c5d9b6e390840112792.jpg')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[{'category_id': 4, 'bbox': [192.10128784179688, 191.18878173828125, 15.174957275390625, 22.388519287109375], 'score': 0.1731969267129898, 'category': '4'}, {'category_id': 5, 'bbox': [118.27978515625, 401.5374755859375, 2327.72021484375, 326.073486328125], 'score': 0.9391258358955383, 'category': '5'}, {'category_id': 5, 'bbox': [0.0, 525.7344970703125, 1528.667724609375, 246.27685546875], 'score': 0.9079427123069763, 'category': '5'}, {'category_id': 5, 'bbox': [898.4940185546875, 371.75079345703125, 1547.5059814453125, 260.0858154296875], 'score': 0.8581957817077637, 'category': '5'}]\n"
     ]
    }
   ],
   "source": [
    "print(result)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2020-07-20 18:05:55 [INFO]\tThe visualized result is saved as ./output/faster_rcnn_r50_vd/visualize_9a49f25c5d9b6e390840112792.jpg\n"
     ]
    }
   ],
   "source": [
    "pdx.det.visualize('PaddleDetection/dataset/coco/val2017/9a49f25c5d9b6e390840112792.jpg', result, threshold=0.9, save_dir='./output/faster_rcnn_r50_vd')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "![file](https://ai-studio-static-online.cdn.bcebos.com/cb547c105145430ca0524339e02554ef208e8460ba6c4f9baee2a65720119b96)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 模型导出\n",
    "> 注意：这里`faster_rcnn_r50_vd`可以顺利完成模型优化opt，而在[手把手教你部署移动端迁移学习模型（PaddleX、Paddle-Lite）](https://aistudio.baidu.com/aistudio/projectdetail/613622)一文中曾介绍过`faster_rcnn_r50_fpn`模型优化失败，原因是paddle升级了generate_proposals op支持RpnRoisLod输出，lite还没有及时更新。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "!paddlex --export_inference --model_dir=output/faster_rcnn_r50_vd/best_model --save_dir=./inference_model/faster_rcnn_r50_vd"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "OPs in the input model include:\r\n",
      "                                        OP_name      Host       X86      CUDA       ARM    OpenCL      FPGA       NPU       XPU     RKNPU       APU       Any       Unk\r\n",
      "                                 affine_channel                                       Y                                                                                \r\n",
      "                               anchor_generator                                       Y                                                                                \r\n",
      "                                       box_clip                                       Y                                                                                \r\n",
      "                                      box_coder                                       Y         Y                                                                      \r\n",
      "                                         conv2d                   Y         Y         Y         Y         Y                                                            \r\n",
      "                                elementwise_add                   Y         Y         Y         Y         Y                                                            \r\n",
      "                                elementwise_div                                       Y                                                                                \r\n",
      "                                           feed         Y                   Y                             Y                                                            \r\n",
      "                                          fetch         Y                                                 Y                                                            \r\n",
      "                             generate_proposals                                       Y                                                                                \r\n",
      "                                            mul                   Y         Y         Y                                                                                \r\n",
      "                                 multiclass_nms         Y                                                 Y                                                            \r\n",
      "                                         pool2d                   Y         Y         Y         Y         Y                                                            \r\n",
      "                                           relu                   Y         Y         Y         Y                                                                      \r\n",
      "                                       reshape2         Y         Y                             Y                                                                      \r\n",
      "                                      roi_align                                       Y                                                                                \r\n",
      "                                sequence_expand                                       Y                                                                                \r\n",
      "                                        sigmoid                                       Y         Y                                                                      \r\n",
      "                                          slice                   Y                   Y         Y                                                                      \r\n",
      "                                        softmax                   Y         Y         Y                                                                                \r\n",
      "Paddle-Lite supports this model!\r\n"
     ]
    }
   ],
   "source": [
    "!paddle_lite_opt --print_model_ops=true  --model_dir=inference_model/faster_rcnn_r50_vd --valid_targets=npu,arm"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# 使用华为NPU预测时的设置\r\n",
    "!paddle_lite_opt \\\r\n",
    "    --model_file=inference_model/faster_rcnn_r50_vd/__model__ \\\r\n",
    "    --param_file=inference_model/faster_rcnn_r50_vd/__params__ \\\r\n",
    "    --optimize_out_type=naive_buffer \\\r\n",
    "    --optimize_out=mobile_npu_model \\\r\n",
    "    --valid_targets=npu,arm "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "# 使用YOLOv3模型训练（PaddleDetection）\n",
    "> 此处可以点击右上角【代码执行器—重启执行器】先释放显存，然后再继续运行后续cell，避免报错"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 修改以下文件中的分类数\n",
    "- PaddleDetection/configs/yolov3_darknet.yml\n",
    "- PaddleDetection/ppdet/modeling/anchor_heads/yolo_head.py\n",
    "- PaddleDetection/ppdet/data/reader.py\n",
    "- PaddleDetection/ppdet/data/transform/batch_operators.py\n",
    "> 注意：如果训练时打印的分类数仍然没有改过来，需要重启一下环境\n",
    "\n",
    "参考配置文件\n",
    "- `/home/aistudio/PaddleDetection/configs/yolov3_darknet.yml`\n",
    "```\n",
    "architecture: YOLOv3\n",
    "use_gpu: true\n",
    "max_iters: 150000\n",
    "log_smooth_window: 200\n",
    "save_dir: output\n",
    "snapshot_iter: 10000\n",
    "metric: COCO\n",
    "pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/DarkNet53_pretrained.tar\n",
    "weights: output/yolov3_darknet/model_final\n",
    "num_classes: 20\n",
    "use_fine_grained_loss: false\n",
    "\n",
    "YOLOv3:\n",
    "  backbone: DarkNet\n",
    "  yolo_head: YOLOv3Head\n",
    "\n",
    "DarkNet:\n",
    "  norm_type: sync_bn\n",
    "  norm_decay: 0.\n",
    "  depth: 53\n",
    "\n",
    "YOLOv3Head:\n",
    "  anchor_masks: [[6, 7, 8], [3, 4, 5], [0, 1, 2]]\n",
    "  anchors: [[36, 297], [5, 94], [5, 15],\n",
    "                [9, 594], [45, 20], [4, 38],\n",
    "                [8, 31], [13, 12], [570, 141]]\n",
    "  norm_decay: 0.\n",
    "  yolo_loss: YOLOv3Loss\n",
    "  nms:\n",
    "    background_label: -1\n",
    "    keep_top_k: 100\n",
    "    nms_threshold: 0.45\n",
    "    nms_top_k: 1000\n",
    "    normalized: false\n",
    "    score_threshold: 0.01\n",
    "\n",
    "YOLOv3Loss:\n",
    "  # batch_size here is only used for fine grained loss, not used\n",
    "  # for training batch_size setting, training batch_size setting\n",
    "  # is in configs/yolov3_reader.yml TrainReader.batch_size, batch\n",
    "  # size here should be set as same value as TrainReader.batch_size\n",
    "  batch_size: 8\n",
    "  ignore_thresh: 0.7\n",
    "  label_smooth: true\n",
    "\n",
    "LearningRate:\n",
    "  base_lr: 0.000125\n",
    "  schedulers:\n",
    "  - !PiecewiseDecay\n",
    "    gamma: 0.1\n",
    "    milestones:\n",
    "    - 80000\n",
    "    - 140000\n",
    "  - !LinearWarmup\n",
    "    start_factor: 0.\n",
    "    steps: 4000\n",
    "\n",
    "OptimizerBuilder:\n",
    "  optimizer:\n",
    "    momentum: 0.9\n",
    "    type: Momentum\n",
    "  regularizer:\n",
    "    factor: 0.0005\n",
    "    type: L2\n",
    "\n",
    "_READER_: 'yolov3_reader.yml'\n",
    "```\n",
    "- `/home/aistudio/PaddleDetection/configs/yolov3_reader.yml`\n",
    "```\n",
    "TrainReader:\n",
    "  inputs_def:\n",
    "    fields: ['image', 'gt_bbox', 'gt_class', 'gt_score']\n",
    "    num_max_boxes: 50\n",
    "  dataset:\n",
    "    !COCODataSet\n",
    "      image_dir: train2017\n",
    "      anno_path: annotations/instances_train2017.json\n",
    "      dataset_dir: dataset/coco\n",
    "      with_background: false\n",
    "  sample_transforms:\n",
    "    - !DecodeImage\n",
    "      to_rgb: True\n",
    "      with_mixup: True\n",
    "    - !MixupImage\n",
    "      alpha: 1.5\n",
    "      beta: 1.5\n",
    "    - !ColorDistort {}\n",
    "    - !RandomExpand\n",
    "      fill_value: [123.675, 116.28, 103.53]\n",
    "    - !RandomCrop {}\n",
    "    - !RandomFlipImage\n",
    "      is_normalized: false\n",
    "    - !NormalizeBox {}\n",
    "    - !PadBox\n",
    "      num_max_boxes: 50\n",
    "    - !BboxXYXY2XYWH {}\n",
    "  batch_transforms:\n",
    "  - !RandomShape\n",
    "    sizes: [320, 352, 384, 416, 448, 480, 512, 544, 576, 608]\n",
    "    random_inter: True\n",
    "  - !NormalizeImage\n",
    "    mean: [0.3914940814114446, 0.3605475730949753, 0.36263685530651174]\n",
    "    std: [0.11077973580477549, 0.10994100883809227, 0.10480770290045718]\n",
    "    is_scale: True\n",
    "    is_channel_first: false\n",
    "  - !Permute\n",
    "    to_bgr: false\n",
    "    channel_first: True\n",
    "  # Gt2YoloTarget is only used when use_fine_grained_loss set as true,\n",
    "  # this operator will be deleted automatically if use_fine_grained_loss\n",
    "  # is set as false\n",
    "  - !Gt2YoloTarget\n",
    "    anchor_masks: [[6, 7, 8], [3, 4, 5], [0, 1, 2]]\n",
    "    anchors: [[10, 13], [16, 30], [33, 23],\n",
    "              [30, 61], [62, 45], [59, 119],\n",
    "              [116, 90], [156, 198], [373, 326]]\n",
    "    downsample_ratios: [32, 16, 8]\n",
    "  batch_size: 16\n",
    "  shuffle: true\n",
    "  mixup_epoch: 250\n",
    "  drop_last: true\n",
    "  worker_num: 8\n",
    "  bufsize: 16\n",
    "  use_process: true\n",
    "\n",
    "\n",
    "EvalReader:\n",
    "  inputs_def:\n",
    "    fields: ['image', 'im_size', 'im_id']\n",
    "    num_max_boxes: 50\n",
    "  dataset:\n",
    "    !COCODataSet\n",
    "      image_dir: val2017\n",
    "      anno_path: annotations/instances_val2017.json\n",
    "      dataset_dir: dataset/coco\n",
    "      with_background: false\n",
    "  sample_transforms:\n",
    "    - !DecodeImage\n",
    "      to_rgb: True\n",
    "    - !ResizeImage\n",
    "      target_size: 608\n",
    "      interp: 2\n",
    "    - !NormalizeImage\n",
    "      mean: [0.3914940814114446, 0.3605475730949753, 0.36263685530651174]\n",
    "      std: [0.11077973580477549, 0.10994100883809227, 0.10480770290045718]\n",
    "      is_scale: True\n",
    "      is_channel_first: false\n",
    "    - !PadBox\n",
    "      num_max_boxes: 50\n",
    "    - !Permute\n",
    "      to_bgr: false\n",
    "      channel_first: True\n",
    "  batch_size: 8\n",
    "  drop_empty: false\n",
    "  worker_num: 8\n",
    "  bufsize: 16\n",
    "\n",
    "TestReader:\n",
    "  inputs_def:\n",
    "    image_shape: [3, 608, 608]\n",
    "    fields: ['image', 'im_size', 'im_id']\n",
    "  dataset:\n",
    "    !ImageFolder\n",
    "      anno_path: annotations/instances_val2017.json\n",
    "      with_background: false\n",
    "  sample_transforms:\n",
    "    - !DecodeImage\n",
    "      to_rgb: True\n",
    "    - !ResizeImage\n",
    "      target_size: 608\n",
    "      interp: 2\n",
    "    - !NormalizeImage\n",
    "      mean: [0.3914940814114446, 0.3605475730949753, 0.36263685530651174]\n",
    "      std: [0.11077973580477549, 0.10994100883809227, 0.10480770290045718]\n",
    "      is_scale: True\n",
    "      is_channel_first: false\n",
    "    - !Permute\n",
    "      to_bgr: false\n",
    "      channel_first: True\n",
    "  batch_size: 1\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 模型训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "!cd cocoapi/PythonAPI && make install"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# 如果训练时出现No module named ‘pycocotools’报错，卸载pycocotools库再重装\n",
    "!pip uninstall pycocotools -y\n",
    "!pip install pycocotools"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "env: CUDA_VISIBLE_DEVICES=0\n"
     ]
    }
   ],
   "source": [
    "%set_env CUDA_VISIBLE_DEVICES=0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "/home/aistudio/PaddleDetection\n"
     ]
    }
   ],
   "source": [
    "%cd /home/aistudio/PaddleDetection/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "%run tools/train.py -c configs/yolov3_darknet.yml --eval"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2020-07-20 18:33:07,562-INFO: Loading parameters from output/yolov3_darknet/model_final...\n",
      "2020-07-20 18:33:10,285-INFO: Not found annotation file annotations/instances_val2017.json, load coco17 categories.\n",
      "2020-07-20 18:33:10,540-INFO: Infer iter 0\n",
      "2020-07-20 18:33:10,593-INFO: Detection bbox results save in output/9a49f25c5d9b6e390840112792.jpg\n"
     ]
    }
   ],
   "source": [
    "%run tools/infer.py -c configs/yolov3_darknet.yml \\\r\n",
    "                    --infer_img=dataset/coco/val2017/9a49f25c5d9b6e390840112792.jpg \\\r\n",
    "                    --output_dir=output/ \\\r\n",
    "                    --draw_threshold=0.5 \\\r\n",
    "                    -o weights=output/yolov3_darknet/model_final"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "![file](https://ai-studio-static-online.cdn.bcebos.com/f373e787122b4eb59ebb0f0fc0ab80ebbd0dc67525284a39b75f8e83b61acbfa)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 模型导出\n",
    "训练得到一个满足要求的模型后，如果想要将该模型接入到C++预测库或者Serving服务，需要通过tools/export_model.py导出该模型。\n",
    "[参考链接](https://github.com/PaddlePaddle/PaddleDetection/blob/release/0.2/docs/advanced_tutorials/inference/EXPORT_MODEL.md)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2020-07-18 19:08:20,346-INFO: Loading parameters from output/yolov3_darknet/model_final...\n",
      "2020-07-18 19:08:23,078-INFO: save_inference_model pruned unused feed variables im_id\n",
      "2020-07-18 19:08:23,080-INFO: Export inference model to ./inference_model/yolov3_darknet, input: ['image', 'im_size'], output: ['multiclass_nms_0.tmp_0']...\n"
     ]
    }
   ],
   "source": [
    "# 导出YOLOv3模型\r\n",
    "%run tools/export_model.py -c configs/yolov3_darknet.yml \\\r\n",
    "        --output_dir=./inference_model \\\r\n",
    "        -o weights=output/yolov3_darknet/model_final"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "!paddle_lite_opt \\\n",
    "    --model_file=inference_model/yolov3_darknet/__model__ \\\n",
    "    --param_file=inference_model/yolov3_darknet/__params__ \\\n",
    "    --optimize_out_type=naive_buffer \\\n",
    "    --optimize_out=model \\\n",
    "    --valid_targets=npu,arm "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 移动端部署\n",
    "- Android开发环境：Android Studio on Ubuntu 18.04 64-bit\n",
    "- 移动端设备：华为Mate20 Pro\n",
    "- 部署过程参考：[手把手教你部署移动端迁移学习模型（PaddleX、Paddle-Lite）](https://aistudio.baidu.com/aistudio/projectdetail/613622)\n",
    "\t- 要准备的模型文件`model.nb`和参考标签文件`fabricflaw-labels.txt`***（注意：标签不支持中文）***\n",
    "    ```\n",
    "    background\n",
    "    hole\n",
    "    dirty\n",
    "    three yarn\n",
    "    knot\n",
    "    slab yarn\n",
    "    centipede\n",
    "    neps\n",
    "    coarse warp\n",
    "    loosen warp\n",
    "    broken warp\n",
    "    hanged warp\n",
    "    coarse pick\n",
    "    looped weft\n",
    "    stain\n",
    "    knot warp\n",
    "    broken spandex\n",
    "    thin thick place\n",
    "    smash\n",
    "    others\n",
    "    ```\n",
    "\t- 修改`PaddleLite-android-demo/yolo_detection_demo/app/src/main/res/values/strings.xml`\n",
    "\t![file](https://ai-studio-static-online.cdn.bcebos.com/fc95e799a1aa4c158d798e09916809654aa5caaff3dd47be9fab754fd6d8e92d)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "# 小结\n",
    "- 即使比较复杂的目标检测场景，用PaddleX的FasterRCNN进行快速迁移学习的效果还是不错的\n",
    "- PaddleDetection模型库要比PaddleX丰富很多，但是dcn、fpn这些算子目前Paddle-Lite并不支持，因此面向移动端部署时，选择比较有限\n",
    "- 在要求检测速度和小目标检测准确度兼具的工业质检场景，目前在Paddle-Lite-Demo中选择比较有限，正如官方文档所说，本文使用的`yolov3_darknet`更适合服务端的部署，如果不使用PaddleSlim，预测速度会掉到 1FPS 左右；而使用`mobilenet`做`backbone`时，又会降低准确率。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "PaddlePaddle 1.7.2 (Python 3.5)",
   "language": "python",
   "name": "py35-paddle1.2.0"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
